﻿using System.Net;
using DotNetty.Codecs;
using DotNetty.Codecs.Compression;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Sockets;
using FileClient.Core.Client.Handlers;
using FileClient.Core.Configuration;
using FileClient.Core.Utils;
using FileCommon.Codecs;
using FileCommon.Models;
using FileEfCore.DbContexts;
using FileEfCore.FileInfo;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace FileClient.Core.Client;

public class DefaultFileServiceClient : IFileServiceClient
{
    private readonly ILogger<DefaultFileServiceClient> _logger;
    private readonly IBootstrapProvider _bootstrapProvider;
    private readonly FileServerOption _fileServerOption;
    public event EventHandler<TransferEventArgs> OnTransferHandle;

    public event EventHandler<TransferEventArgs> OnTransferCompleteHandle;

    public event EventHandler<ExceptionTransferEventArgs> OnTransferExceptionHandle;

    public DefaultFileServiceClient(
        ILogger<DefaultFileServiceClient> logger,
        IOptions<FileServerOption> fileServerOption,
        IBootstrapProvider bootstrapProvider)
    {
        _logger = logger;
        _fileServerOption = fileServerOption.Value;
        _bootstrapProvider = bootstrapProvider;
    }

    public async Task FileOperation(string localFileName, string serverFileName, string fileId,
        OperationType operationType)
    {

        if (string.IsNullOrWhiteSpace(serverFileName) && string.IsNullOrWhiteSpace(fileId))
        {
            throw new Exception("请指定要下载的文件名或是文件Id");
        }
        
        if (!EndpointChecker.IsValidFileServer(_fileServerOption.FileServerEndpoint))
        {
            throw new Exception("设置的文件服务地址不正确");
        }

        switch (operationType)
        {
            case OperationType.Upload:
                if (!File.Exists(localFileName))
                {
                    throw new Exception($"不存在本地文件{localFileName}");
                }

                break;
            case OperationType.Download:
                break;
            default:
                throw new Exception("文件操作类型不正确");
                break;
        }

        var bootstrap = _bootstrapProvider.CreateClientBootstrap();
        bootstrap
            .Handler(new ActionChannelInitializer<ISocketChannel>(c =>
            {
                var pipeline = c.Pipeline;
                var fileTransferClientHandler = new FileTransferClientHandler(localFileName, serverFileName, fileId,
                    _fileServerOption.ProgressStep,
                    _fileServerOption.FilePieceSize,
                    operationType);

                fileTransferClientHandler.OnTransferHandle += (sender, args) =>
                {
                    using var scope = App.ServiceProvider.CreateScope();
                    using var dbContext = scope.ServiceProvider.GetRequiredService<FileDbContext>();
                    var uploadFile = dbContext.UploadFiles
                        .FirstOrDefault(p =>
                            p.LocalFileName == args.FileName && p.ServerFileName == args.ServerFileName);
                    if (uploadFile == null)
                    {
                        uploadFile = new UploadFile()
                        {
                            LocalFileName = args.FileName,
                            ServerFileName = args.ServerFileName,
                            ProgressRate = args.ProgressRate,
                            ProgressStatus = args.ProgressStatus,
                            OperationType = args.OperationType,
                            OperationDateTime = DateTime.Now,
                            LastUpdateTime = DateTime.Now,
                            FileLength = args.FileLength,
                            MD5Hash = args.MD5Hash
                        };
                        dbContext.Add(uploadFile);
                        dbContext.SaveChanges();
                    }
                    else
                    {
                        uploadFile.ProgressStatus = args.ProgressStatus;
                        uploadFile.ProgressRate = args.ProgressRate;
                        uploadFile.FileLength = args.FileLength;
                        uploadFile.Remark = args.ErrorMessage;
                        uploadFile.LastUpdateTime = DateTime.Now;
                        dbContext.Update(uploadFile);
                        dbContext.SaveChanges();
                    }
                    
                    OnTransferHandle?.Invoke(this, args);
                };
                fileTransferClientHandler.OnTransferCompleteHandle += (sender, args) =>
                {
                    using var scope = App.ServiceProvider.CreateScope();
                    using var dbContext = scope.ServiceProvider.GetRequiredService<FileDbContext>();
                    var uploadFile = dbContext.UploadFiles.FirstOrDefault(p =>
                        p.LocalFileName == args.FileName && p.ServerFileName == args.ServerFileName);
                    if (uploadFile != null)
                    {
                        uploadFile.ProgressStatus = args.ProgressStatus;
                        uploadFile.ProgressRate = args.ProgressRate;
                        uploadFile.Remark = args.ErrorMessage;
                        uploadFile.LastUpdateTime = DateTime.Now;
                        dbContext.Update(uploadFile);
                        dbContext.SaveChanges();
                    }
                    
                    OnTransferCompleteHandle?.Invoke(this, args);
                };
                fileTransferClientHandler.OnTransferExceptionHandle += (sender, args) =>
                {
                    using var scope = App.ServiceProvider.CreateScope();
                    using var dbContext = scope.ServiceProvider.GetRequiredService<FileDbContext>();
                    var uploadFile = dbContext.UploadFiles.FirstOrDefault(p =>
                        p.LocalFileName == args.FileName && p.ServerFileName == args.ServerFileName);
                    if (uploadFile != null)
                    {
                        uploadFile.ProgressStatus = ProgressStatus.Error;
                        uploadFile.Remark = args.Exception.Message;
                        uploadFile.LastUpdateTime = DateTime.Now;
                        dbContext.Update(uploadFile);
                        dbContext.SaveChanges();
                    }
                    
                    OnTransferExceptionHandle?.Invoke(this, args);
                };
                pipeline.AddLast(new LengthFieldPrepender(4));
                pipeline.AddLast(new LengthFieldBasedFrameDecoder(int.MaxValue, 0, 4, 0, 4));
                pipeline.AddLast(ZlibCodecFactory.NewZlibEncoder(ZlibWrapper.Gzip));
                pipeline.AddLast(ZlibCodecFactory.NewZlibDecoder(ZlibWrapper.Gzip));
                // 编解码器
                pipeline.AddLast(new ObjectDecoder());
                pipeline.AddLast(new ObjectEncoder());
                // 文件传输处理器
                pipeline.AddLast(fileTransferClientHandler);
            }));
        await bootstrap.ConnectAsync(new IPEndPoint(IPAddress.Parse(_fileServerOption.Ip),
            _fileServerOption.Port));
    }
}